#pragma once

#include <cppjieba/Jieba.hpp>
#include <utfcpp/utf8.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <map>
#include <set>
#include <queue>
#include <tuple>
#include <nlohmann/json.hpp>
#include "MyTask.h"

using std::ifstream;
using std::map;
using std::set;
using std::istringstream;
using std::cout;
using std::cerr;
using std::endl;
using std::priority_queue;
using std::pair;
using std::tuple;

using wordType = int;

struct CandidateWord
{
    string word;
    int editDistance;
    int freq;
};

inline bool chinese_word(uint64_t codePoint)
{
    return (codePoint >= 0x4E00 && codePoint <= 0x9FFF) ||
           (codePoint >= 0x3400 && codePoint <= 0x4DBF) ||
           (codePoint >= 0x20000 && codePoint <= 0x2A6DF) ||
           (codePoint >= 0x2A700 && codePoint <= 0x2B73F) ||
           (codePoint >= 0x2B740 && codePoint <= 0x2B81F) ||
           (codePoint >= 0x2B820 && codePoint <= 0x2CEAF) ||
           (codePoint >= 0x2CEB0 && codePoint <= 0x2EBEF) ||
           (codePoint >= 0x30000 && codePoint <= 0x3134F);
}

inline bool english_alpah(uint64_t codePoint)
{
    return (codePoint >= 0x41 && codePoint <= 0x5A) ||
           (codePoint >= 0x61 && codePoint <= 0x7F);
}

inline void readIndex(ifstream &ifs, map<string, vector<int>> &index)
{
        string line;
        while(getline(ifs, line))
        {
            istringstream iss{line};
            string word;
            iss >> word;
            index.emplace(word, vector<int>{});
            int idx;
            while(iss >> idx)
            {
                index[word].push_back(idx);
            }
        }   
}

inline void readDict(ifstream &ifs, map<string, int> &dict, map<int, string> &lineMap)
{
    string line;
    int lineNum = 1;
    while(getline(ifs, line))
    {
        istringstream iss{line};
        string word;
        int frequency;
        iss >> word;
        iss >> frequency;
        dict[word] = frequency;
        lineMap[lineNum] = word;
        lineNum++;
    }
}

inline int minDistance(string s1, string s2)
{
    int m = s1.size();
    int n = s2.size();

    vector<vector<int>> dp(m+1, vector<int>(n+1));

    for(int i = 0; i < m; i++)
    {
        dp[i][0] = i;
    }
    for(int i = 0; i < n; i++)
    {
        dp[0][i] = i;
    }

    for(int i = 1; i <= m; ++i)
    {
        for(int j = 1; j <= n; ++j)
        {
            int insert = dp[i][j-1] + 1;
            int remove = dp[i-1][j] + 1;
            int replace = dp[i-1][j-1] + (s1[i-1] != s2[j-1]);
            dp[i][j] = std::min({insert, remove, replace});
        }
    }
    return dp[m][n];
}

class Compare
{
public:
    Compare() = default;

    bool operator()(const CandidateWord &c1, const CandidateWord &c2) const
    {
        if(c1.editDistance != c2.editDistance)
        {
            return c1.editDistance > c2.editDistance; // 优先低编辑距离，优先级队列为大根堆，故 '>'
        }

        if(c1.freq != c2.freq)
        {
            return c1.freq < c2.freq; // 优先高词频 故 '<'
        }

        return c1.word > c2.word; // 优先低字典序 故 '>'

    }
};

class KeyRecommandTask
: public MyTask
{
public:
    KeyRecommandTask(const string &msg, const TcpConnectionPtr &con)
    : m_msg(msg), m_con(con)
    {
        ifstream ifsEnIndex("../../libfile/en_index.dat");
        ifstream ifsEnDict("../../libfile/en_dict.dat");
        ifstream ifsCnIndex("../../libfile/cn_index.dat");
        ifstream ifsCnDict("../../libfile/cn_dict.dat");

        if(!ifsEnIndex || !ifsEnDict || !ifsCnIndex || !ifsCnDict) {
            cerr << "词典文件读取失败" << endl;
            return;
        }

        readIndex(ifsEnIndex, m_enIndex);
        readDict(ifsEnDict, m_enDict, m_enLineMap);
        readIndex(ifsCnIndex, m_cnIndex);
        readDict(ifsCnDict, m_cnDict, m_cnLineMap);
#ifdef DEBUG
        cout << "读取库文件成功" << endl;
#endif


    }
    
    virtual void process(MyThread *myTh) override
    {   
#ifdef DEBUG
        cout << "开始执行关键字推荐任务" << endl;
#endif

        auto js = myTh->cache.get(m_msg);
        if(!js.empty())
        {
            cout << "从缓存中得到数据" << endl;
            m_con->sendInLoop(js.dump());
            return;
        }

        // set<string> candidateWordSet; // 候选词集合, 确定是英文还是中文
        // set<std::pair<string, int>> candidateWordSet; // 0代表英文，1代表中文
        set<CandidateWord, Compare> candidateWordSet; // 0代表英文，1代表中文
        const char *begin = m_msg.c_str();
        const char *end = m_msg.c_str() + m_msg.size();


        // 根据每一个utf8字符->查库，得到候选词集合
        while(begin != end)
        {
            uint64_t codePoint = utf8::next(begin, end);
            string alpha;
            utf8::append(codePoint, alpha);

            if(english_alpah(codePoint))
            {

                if(codePoint >= 0x41 && codePoint <= 0x5A)
                {
                    alpha[0] = std::tolower(alpha[0]);
                }
                auto curAlphaIt = m_enIndex.find(alpha);
                if(curAlphaIt == m_enIndex.end())
                {
                    cerr << "查找单词出错" << endl;
                    return;
                }
                for(const auto &idx : curAlphaIt->second)
                {
                    CandidateWord cw;
                    cw.word = m_enLineMap[idx];
                    cw.editDistance = minDistance(m_msg, cw.word);
                    cw.freq = m_enDict[cw.word];
                    candidateWordSet.insert(cw);
                }
            }else if(chinese_word(codePoint))
            {
                auto curAlphaIt = m_cnIndex.find(alpha);
                if(curAlphaIt == m_cnIndex.end())
                {
                    cerr << "查找文字出错" << endl;
                    return;
                }
                for(const auto &idx : curAlphaIt->second)
                {
                    CandidateWord cw;
                    cw.word = m_cnLineMap[idx];
                    cw.editDistance = minDistance(m_msg, cw.word);
                    cw.freq = m_cnDict[cw.word];
                    candidateWordSet.insert(cw);
                }
                cout << endl;
            }else
            {
                continue;
            }
        }

#ifdef DEBUG
        cout << "得到候选词集合" << endl;
#endif

        // 求出每个候选词与关键字的编辑距离，并用优先级队列进行排序
        auto getCompareResult = [&candidateWordSet](string &msg, map<string, int> &endict, map<string, int> &cndict)
        {
            priority_queue<CandidateWord, vector<CandidateWord>, Compare> candDistQueue{Compare{}};
            int count = 0;
            for(const auto &candidateWord : candidateWordSet)
            {
                count++;
                candDistQueue.push(candidateWord);
                if(count % 1000 == 0) cout << "Processed " << count << " candidate words" << endl;
            }
            return candDistQueue;
        };

        auto candDistQueue = getCompareResult(m_msg, m_enDict, m_cnDict);
#ifdef DEBUG
        cout << "得到候选词优先级队列" << endl;
#endif

        nlohmann::json data = nlohmann::json::array();
        int queueSize = std::min(5, (int)candDistQueue.size());
        for(int i = 0; i < queueSize; i++)
        {
            data.emplace_back(candDistQueue.top().word);
            candDistQueue.pop();
        }
 #ifdef DEBUG
        cout << "生成 json" << endl;
#endif       
        myTh->cache.put(m_msg, data);
        myTh->patch.put(m_msg, data);
        m_con->sendInLoop(data.dump());
    }


    virtual ~KeyRecommandTask() override
    {}

private:
    string m_msg;
    TcpConnectionPtr m_con;

    map<string, vector<int>> m_enIndex;        
    map<string, int> m_enDict;// 词，以及词频
    map<int, string> m_enLineMap; // 行号，词
    map<string, vector<int>> m_cnIndex;
    map<string, int> m_cnDict;      
    map<int, string> m_cnLineMap; // 行号，词
};
